﻿package systems.traer 
{
	import flash.display.Sprite;
	import flash.geom.Vector3D;
	import flash.utils.getTimer;
	
	public class ParticleSystem
	{
		public static const RUNGE_KUTTA:int = 0;
		public static const MODIFIED_EULER:int = 1;
		
		protected static var DEFAULT_GRAVITY:Number = 0;
		protected static var DEFAULT_DRAG:Number = 0.01;
		
		private var _particles:Vector.<Particle>;
		private var _springs:Vector.<Spring>;
		private var _attractions:Vector.<Attraction>;
		private var _customForces:Vector.<Force> = new Vector.<Force>();
		
		private var _integrator:Integrator;
		private var _gravity:Vector3D;
		
		private var _drag:Number;
		
		public function ParticleSystem( gravity:Vector3D, drag:Number = .01 )
		{
			
			integrator = new RungeKuttaIntegrator(this);
			
			particles = new Vector.<Particle>();
			springs = new Vector.<Spring>();
			attractions = new Vector.<Attraction>();
			
			this.gravity = gravity.clone();
			this.drag = drag;
			
		}
		
		public final function setGravity3D(gx:Number, gy:Number, gz:Number):void 
		{
			
			gravity.x = gx;
			gravity.y = gy;
			gravity.z = gz;
			
		}
		
		public final function setGravity2D(gy:Number):void {
			gravity.y = gy;
		}
		
		
		public final function tick(t:Number = 1):void 
		{
			integrator.step(t);
		}
		
		public final function makeParticle(mass:Number = 1, x:Number = 0, y:Number = 0, z:Number = 0):Particle
		{
			var p:Particle = new Particle(mass);
			p.position.x = x
			p.position.y = y
			p.position.z = z
			
			particles.push(p);
			return p;
		}
		public final function makeSpring(particleA:Particle, particleB:Particle, ks:Number, d:Number, r:Number):Spring
		{
			var s:Spring = new Spring(particleA, particleB, ks, d, r);
			springs.push(s);
			return s;
		}
		public final function makeAttraction(particleA:Particle, particleB:Particle, k:Number, minDistance:Number):Attraction
		{
			var a:Attraction = new Attraction(particleA, particleB, k,minDistance);
			attractions.push(a);
			return a;
		}
		public final function clear():void 
		{
			particles = new Vector.<Particle>();
			springs = new Vector.<Spring>();
			attractions = new Vector.<Attraction>();
		}
		
		public final function applyForces():void
		{
			
			//var time:int = getTimer();
			var i:int;
			var len:int = particles.length;
			var p:Particle;
			var isZero:Boolean = ( gravity.x == 0 && gravity.y == 0 && gravity.z == 0 );
			
			if ( !isZero )
			  {
				  for ( i = 0; i < len; ++i )
				  {
					  particles[i].force = particles[i].force.add( gravity );//TODO retourner un Vcetor3D
				  }
			  }
			
			for ( i = 0; i < len; ++i )
			{
				p = particles[i];
				//p.force.addFromValues( p.velocity.x * -drag, p.velocity.y * -drag, p.velocity.z * -drag );
				
				p.force.x += p.velocity.x * -drag;
				p.force.y += p.velocity.y * -drag;
				p.force.z += p.velocity.z * -drag;
				
			}
			  
			  len = springs.length;
			  for ( i = 0; i < len; i++ )
			  {
				  springs[i].apply();
			  }
			  len = attractions.length;
			  for ( i = 0; i < len; i++ )
			  {
				  attractions[i].apply();
			  }
			  len = customForces.length;
			  for (i = 0; i < len; i++ )
			  {
				  customForces[i].apply();
			  }
			  
			  //trace("time = "+(getTimer()-time))
		  }
		  
		  public final function clearForces():void
		  {
			  var i:int;
			  var len:int = particles.length;
			  for (i = 0; i < len; i++) 
			  {
				particles[i].clearForce();
			  }
		  }
		  
		  public final function numberOfParticles():int {
			  return particles.length;
		  }
		  public final function numberOfSprings():int {
			  return springs.length;
		  }
		  public final function numberOfAttractions():int {
			  return attractions.length;
		  }
		  
		  public final function getParticleById(id:int):Particle {
			  return particles[id];
		  }
		  public final function getSpringById(id:int):Spring {
			  return springs[id];
		  }
		  public final function getAttractionById(id:int):Attraction
		  {
			  return attractions[id];
		  }
		  public final function addCustomForce(f:Force):void 
		  {
			  customForces.push(f);
		  }
		  public final function numberOfCustomForces():int
		  {
			  return customForces.length;
		  }
		  public final function getCustomForceById(id:int):Force 
		  {
			  return customForces[id];
		  }
		  
		  public final function removeCustomForceById(id:int):Force
		  {
			 var f:Force = customForces[id];
			 customForces.splice(id, 1);
			 return f;
		  }
		  public final function removeCustomForce(f:Force):void 
		  {
			 customForces.splice(customForces.lastIndexOf(f), 1);
		  }
		  
		  
		  public final function removeParticleById(id:int):Particle 
		  {
			 //var f:Particle = particles[id];
			 var i:int
			 //for each( var p:Particle in particles ) 
			 for ( i = 0; i < particles.length; i++ ) 
			 {
				 if ( particles[ i ].id == id )
				 {
					particles.splice(i, 1);
				 }
			 }
			 
			
			 return null;
		  }
		  public final function removeParticle(p:Particle):void
		  {
			 particles.splice(particles.lastIndexOf(p), 1);
		  }
		  
		  
		  
		  public final function removeSpringById(id:int):Spring
		  {
			 var f:Spring = springs[id];
			 springs.splice(id, 1);
			 return f;
		  }
		  public final function removeSpring(s:Spring):void
		  {
			 springs.splice(springs.lastIndexOf(s), 1);
		  }
		  
		  
		  
		  public final function removeAttractionById(id:int):Attraction
		  {
			 var f:Attraction = attractions[id];
			 attractions.splice(id, 1);
			 return f;
		  }
		  
		  public final function removeAttraction(a:Attraction):void 
		  {
			 attractions.splice( attractions.lastIndexOf(a), 1 );
		  }
		  
		  public function get particles():Vector.<Particle> { return _particles; }
		  
		  public function set particles(value:Vector.<Particle>):void 
		  {
			  _particles = value;
		  }
		  
		  public function get gravity():Vector3D { return _gravity; }
		  
		  public function set gravity(value:Vector3D):void 
		  {
			  _gravity = value;
		  }
		  
		  public function get drag():Number { return _drag; }
		  
		  public function set drag(value:Number):void 
		  {
			  _drag = value;
		  }
		  
		  public function get springs():Vector.<Spring> { return _springs; }
		  
		  public function set springs(value:Vector.<Spring>):void 
		  {
			  _springs = value;
		  }
		  
		  public function get attractions():Vector.<Attraction> { return _attractions; }
		  
		  public function set attractions(value:Vector.<Attraction>):void 
		  {
			  _attractions = value;
		  }
		  
		  public function get customForces():Vector.<Force> { return _customForces; }
		  
		  public function set customForces(value:Vector.<Force>):void 
		  {
			  _customForces = value;
		  }
		  
		  public function get integrator():Integrator { return _integrator; }
		  
		  public function set integrator(value:Integrator):void 
		  {
			  _integrator = value;
		  }
		  
	}

}